package com.yeskery.nut.aop;

import net.sf.cglib.proxy.Callback;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.InvocationHandler;
import net.sf.cglib.proxy.NoOp;

import java.lang.reflect.Method;
import java.lang.reflect.Modifier;

/**
 * Cglib动态代理工厂
 * @author sprout
 * @version 1.0
 * 2022-08-28 12:23
 */
public class CglibProxyObjectFactory implements ProxyObjectFactory {

    /** 拦截处理对象 */
    private final WrapInvocationHandler wrapInvocationHandler;

    /**
     * 构建Cglib动态代理工厂对象
     * @param wrapInvocationHandler 拦截处理对象
     */
    public CglibProxyObjectFactory(WrapInvocationHandler wrapInvocationHandler) {
        if (wrapInvocationHandler == null) {
            throw new ProxyObjectCreateException("Cglib Proxy InvocationHandler Must Not Be Null.");
        }
        this.wrapInvocationHandler = wrapInvocationHandler;
    }

    @Override
    public Object createProxyInstance(Class<?> clazz) {
        return createEnhancer(clazz).create();
    }

    @Override
    public Object createProxyInstance(Class<?> clazz, Class<?>[] argumentTypes, Object[] arguments) {
        return createEnhancer(clazz).create(argumentTypes, arguments);
    }

    /**
     * 创建增强对象
     * @param clazz 代理类对象
     * @return 增强对象
     */
    private Enhancer createEnhancer(Class<?> clazz) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(clazz);
        enhancer.setCallbacks(new Callback[] {NoOp.INSTANCE, new CglibInvocationHandlerAdapter()});
        enhancer.setCallbackFilter(m -> {
            int modifiers = m.getModifiers();
            return !Modifier.isStatic(modifiers) && !Modifier.isAbstract(modifiers) && Modifier.isPublic(modifiers) ? 1 : 0;
        });
        return enhancer;
    }

    /**
     * Cglib动态代理拦截处理适配器
     * @author sprout
     * @version 1.0
     * 2022-08-28 14:49
     */
    private class CglibInvocationHandlerAdapter implements InvocationHandler {

        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            return wrapInvocationHandler.invoke(proxy, method, args);
        }
    }
}
